package nachos.vm;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import nachos.machine.Lib;
import nachos.machine.Machine;
import nachos.machine.Processor;
import nachos.machine.TranslationEntry;

public class InvertPageTable {
	private TranslationEntry[] invertedPageTable;
	// freelist to trace free pages in the page table
	protected LinkedList<Integer> freelist = new LinkedList<Integer>();
	private Map<Long,TranslationEntry> owner = new HashMap<Long,TranslationEntry>();
	// LRU page replacement
	protected List<TranslationEntry> replacement = new LinkedList<TranslationEntry>();

	public InvertPageTable(){
		int phypagenum = Machine.processor().getNumPhysPages();
		invertedPageTable = new TranslationEntry[phypagenum];
		for(int i=0; i<phypagenum;i++){
			// fill PageTable with invalid entries
			invertedPageTable[i] = new TranslationEntry(-1, i, false, false, false, false);
			// add index to free list
			freelist.add(i);
			replacement.add(0,invertedPageTable[i]);
		}
		replacement.clear(); //??????
	}
	
	public void addEntry (int processID, TranslationEntry entry){
		invertedPageTable[entry.ppn] = entry;
		long o = ((long) processID << 32) | entry.vpn;
		
		if(entry.vpn != (int)(o & 0xFFFFFFFFL)){
			Lib.debug('x', "virtual page number error -- addEntry");
		}
		
		owner.put(o, entry);
	}
	
	public TranslationEntry getEntry(int physical){
		return invertedPageTable[physical];
	}
	
	public TranslationEntry getEntry(int processID, int virtual){
		return owner.get(((long) processID << 32) | virtual);
	}
	
	public void deleteEntry (long o){
		if(owner.get(o)!=null){
			TranslationEntry entry = owner.remove(o);
			invertedPageTable[entry.ppn] = new TranslationEntry(-1, entry.ppn, false, false, false, false);
			freelist.addLast(entry.ppn);
			replacement.remove(entry);
		}
	}
	
	// remove entry by PID
	public void deleteEntry(int processID){
		ArrayList<Long> ready_for_remove= new ArrayList<Long>();
		
		for(long o:owner.keySet()){
			if((int) (o >> 32) == processID)
				ready_for_remove.add(o);
		}
		
		for(long o:ready_for_remove)
			deleteEntry(o);
	}
	
	public int getOwnerPid (int physical){
		int res = -1;
		for(long o:owner.keySet()){
			if(physical != owner.get(o).ppn)
				continue;
			else {
				res = (int) (o >> 32);
				break;
			}
		}
		return res;
	}
	
	public void resetPage(int physical){
		byte[] memory = Machine.processor().getMemory();
        int temp  = Processor.makeAddress(physical, 0);
        Arrays.fill(memory, temp, temp+(Processor.pageSize-1), (byte)0);
	}

}
